Skip to content

API V2 Design Review #120

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ImGajeed76
Copy link
Collaborator

API V2 Design Review

This PR outlines the proposed API V2 design. Please review and provide feedback on the changes before we proceed with implementation.

Key feedback areas:

  • Proposed API changes
  • Breaking changes from V1
  • Suggestions for improvement

All feedback will be discussed and incorporated based on team consensus.

Copy link

vercel bot commented Feb 15, 2025

Deployment failed with the following error:

Creating the Deployment Timed Out.

@ImGajeed76
Copy link
Collaborator Author

If you know you want to change something but didn't have time yet, please write a quick comment. If you think its good like that leave a thumbs up.

@solonovamax
Copy link

  • for the bearer token auth type, it isn't fully clear that it can optionally be a JWT token
    smth like

This can be a JWT, or any other kind of token. The type of token used is left up to the implementation.
might be better

  • I don't think that a "default" server api url should be listed in the openapi spec

  • I think the following http codes should all be suported:

    • 400 (Bad Request)
    • 401 (Unauthorized)
    • 403 (Forbidden)
    • 406 (Not Acceptable)
    • 429 (Too Many Requests)
    • 500 (Internal Server Error)
    • 501 (Not Implemented)
  • why was basic authentication removed?

  • somewhere in the auth section for the server information, there should be an optional field that provides a link to a signup page

  • the server information authorization section should list which routes require authorization

  • imo the features section of the server information should be an object rather than an array

  • the coupon query api is still incredibly unclear. it uses a ?q= parameter, and nowhere does it specify where the domain should go.
    it should really just be something like /coupons/site/{domain}/list

  • what happened to the site info route I had?

  • I feel the pagination opject should be shared, and you should use allOf for the schema
    eg. image

  • same applies to the merchant search

  • the PATCH requests are used improperly. according to the HTTP spec, PATCH requests should be sending partial objects, with only the information that is to be updated.
    eg. if the object stored in the database looked something like

    {
        "name": "foo",
        "description": "bar"
    }

    and you were to send a PATCH with

    {
        "name": "foo2",
        "baz": "qux"
    }

    then the afterwards, the object in the database should be

    {
        "name": "foo2",
        "description": "bar",
        "baz": "qux"
    }
  • there is no route for submitting a rating for a coupon

  • a PUT request should really be used to submit things tbh

  • in the filterBy query, something like ?filterBy[discount_value]=, is acceptable, according to the openapi spec

  • what's the difference between a category and a tag for a coupon? why do both exist?

  • BOGO -> BUY_ONE_GET_ONE_FREE

  • an additional OTHER type should be added for the discount type

  • inconsistent use of camel case and snake case. everything should be snake case.

  • honestly, sortBy should be split into sort and sort_order or smth, because all enums have both asc and desc variants.

  • will searchIn ever be actually used?

  • Coupon objects don't have a site field

  • description should be optional for a Coupon

  • there should be a rating field, in addition to the score field
    image

  • imo up_votes and down_votes should be renamed to positive_votes and negative_votes, respectively. but that's just personal preference.

  • minimums and maximums should be set on all relevant fields in the spec

  • imo instead of having
    coupon -(many-to-one)-> merchant
    it should be
    coupon -(many-to-one)-> site -(many-to-one)-> merchant

    where, the site has things like a name, a description, and a domain

  • in many places, there are references to the id of things. however, most times you just need the name of smth. so to avoid additional requests, you should introduce *Reference objects, for example:
    image
    image

  • in the spec, the X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers are only returned for a 200 response. they should always be returned.

  • also, use the responses component list (as well as requestBodies, headers, and parameters):
    image
    image

@ImGajeed76
Copy link
Collaborator Author

Sorry @solonovamax that I haven't looked at it since you posted. But to all others, does no one have an opinion on those changes? (Not that they are bad, btw.)

Refactors the OpenAPI specification to version 2.1.0, introducing significant structural changes and new features based on feedback.

Key changes include:
- Introduction of the `Site` entity (Coupon -> Site -> Merchant relationship).
- Extensive use of reusable components (`parameters`, `schemas`, `responses`, `requestBodies`, `headers`) for better maintainability and consistency.
- Addition of endpoints for managing `Sites` and suggesting changes.
- Replacement of `PATCH` with `POST /{resource}/{id}/suggestions` for clarity.
- Update of coupon voting (`POST /coupons/{id}/votes`).
- Consistent use of `snake_case` and standard HTTP status codes.
- Clarifications and improvements to descriptions, schemas, and parameters based on review feedback.
@ImGajeed76
Copy link
Collaborator Author

Changelog for Syrup API Standard v2.1.0

This version introduces significant changes, including new entities and structural improvements, based on feedback from PR #120.

Changes

  • Data Model: The relationship between Coupons and Merchants has changed. Coupons are now linked to a Site, and each Site is linked to a Merchant.
    • Coupon.merchant (object) replaced with Coupon.site (object).
    • CouponSuggestBody.merchant_id replaced with CouponSuggestBody.site_id.
    • Coupon search filters filter_by[merchant_id], filter_by[merchant_name], filter_by[merchant_domain] replaced with filter_by[site_id] and filter_by[site_domain].
    • Coupon search fields search_in options merchant_name, merchant_domain replaced with site_name, site_domain.
  • Endpoints Renamed/Replaced:
    • PATCH /merchants/{merchant_id} removed. Use POST /merchants/{merchant_id}/suggestions instead (See Rationale below).
    • PATCH /autofill/{domain} removed. Use POST /autofill/{domain} for suggesting new or updated configurations (See Rationale below).
  • Schema Changes:
    • All identifiers (id fields for Coupon, Site, Merchant, AutoFillConfig) are now consistently type: string instead of potentially integer.
    • CouponHistoryResponse.id changed from integer to string.
    • VoteBody.vote_type enum changed from positive/negative to up/down (See Rationale below).
    • DiscountType enum BOGO renamed to BUY_ONE_GET_ONE_FREE.
  • Case Convention: All query parameters, schema properties, and enum values now consistently use snake_case (e.g., filterBy -> filter_by, discountValue -> discount_value, PERCENTAGE_OFF -> PERCENTAGE_OFF (enum values often kept uppercase), startDate -> start_date).
  • Authentication: Basic Authentication support removed from the specification. Only bearer_auth and api_key_auth are defined.

New Features

  • Sites: Introduced a new Site entity representing a specific domain/storefront belonging to a Merchant.
    • Added GET /sites endpoint for searching sites.
    • Added POST /sites endpoint for suggesting new sites.
    • Added GET /sites/{site_id} endpoint to retrieve a specific site.
    • Added POST /sites/{site_id}/suggestions endpoint to suggest changes to an existing site.
  • Merchants Suggestions: Added POST /merchants/{merchant_id}/suggestions endpoint to suggest changes to an existing merchant.
  • Autofill Suggestions: POST /autofill/{domain} now handles suggesting new or updated configurations. Response codes 201 (Created) or 202 (Accepted for update) differentiate.
  • Coupon Voting: Added POST /coupons/{coupon_id}/votes endpoint to allow users to submit up or down votes on coupon validity.
  • Info Endpoint Enhancements:
    • Added optional auth.signup_url field to InfoResponse for providers to link to their signup/API key management page.
    • Added new features enum values corresponding to the new suggestion/voting endpoints (e.g., sites_suggest, coupons_vote).
  • Discount Type: Added OTHER to the DiscountType enum.

Improvements & Fixes

  • Componentization: Heavily refactored the spec to use reusable components ($ref) for:
    • parameters (e.g., QueryParamLimit, PathParamCouponId)
    • requestBodies (e.g., CouponSuggestBody, VoteBody)
    • responses (e.g., BadRequest, NotFound, TooManyRequests)
    • headers (e.g., RateLimitLimit, RetryAfter)
    • schemas (e.g., PaginationInfo, ErrorResponse)
  • Pagination: Implemented a reusable PaginationInfo schema and used allOf in search responses (CouponSearchResponse, SiteSearchResponse, MerchantSearchResponse) for consistency.
  • Error Handling: Defined standard, reusable error responses for common HTTP status codes (400, 401, 403, 404, 406, 429, 500, 501) and applied them consistently across endpoints.
  • Rate Limiting Headers: Rate limit headers (X-RateLimit-*) are now defined on common error responses as well, ensuring they are returned more consistently.
  • Clarity & Descriptions:
    • Improved descriptions for many fields and parameters (e.g., clarifying Bearer token types, filter_by usage, Category vs. Tag difference).
    • Clarified filter_by range syntax (e.g., 10,50, 10,, ,50).
    • Made Coupon.description optional.
  • Constraints: Added minimum and maximum constraints to relevant numeric fields (e.g., limit, discount_value, timeout).
  • Server URL: Changed the server URL to the syrup default (https://db.joinsyrup.com/syrup/v2) and added a comment indicating it's provider-specific (See Rationale below).
  • HTTP Status Codes:
    • Used 201 Created for successful resource creation suggestions (POST /coupons, /sites, /merchants).
    • Used 202 Accepted for successful change suggestions (POST /sites/{id}/suggestions, /merchants/{id}/suggestions) or autofill updates.
    • Added 404 Not Found where applicable (e.g., GET /coupons/{id}).
    • Added 406 Not Acceptable response component.
    • Added 501 Not Implemented response component.
  • Schema Refinements: Embedded full Site and Merchant objects within Coupon and Site respectively to reduce the need for follow-up requests (addresses the intent behind *Reference objects - See Rationale below).

Rationale for Implementation Decisions

This section clarifies why certain feedback points were implemented differently than suggested or were not adopted:

  • Auth Routes in Info: Listing specific authenticated routes in the /info response was not implemented.
    • Reason: OpenAPI defines security requirements per-operation via the security keyword. Duplicating this dynamically in /info is redundant, complex to maintain, and non-standard. The auth.required flag gives a general indication, and the spec itself is the source of truth for which endpoints require authentication.
  • Features Object vs. Array: The features list in /info remains an array.
    • Reason: An array of strings is simpler for clients to check for the presence/absence of a feature flag. While an object offers future extensibility, the current requirement is just listing supported features, making the array sufficient and easier to parse.
  • sortBy Splitting: The sort_by=field:direction format was kept instead of splitting into sort=field and sort_order=asc|desc.
    • Reason: The field:direction format is a common, concise pattern for sorting in APIs. While splitting is also valid, the current format was deemed sufficient and slightly less verbose.
  • rating Field: A separate user rating field was not added to the Coupon schema.
    • Reason: The provider-calculated score field is intended to be the primary indicator of coupon quality, incorporating various factors including user votes (up_votes/down_votes). Adding a separate user_rating field was considered potentially redundant, as clients can derive similar information from the vote counts if needed. The score aims to provide a single, holistic quality measure.
  • Vote Renaming (positive/negative): Kept up_votes/down_votes and the up/down enum values for VoteBody.vote_type.
    • Reason: up_votes/down_votes (and corresponding up/down actions) are widely recognized terms in the context of user feedback and voting systems. Maintaining this common terminology was preferred for familiarity over positive/negative.
  • PUT for Submissions: POST is used for suggestion and voting endpoints, not PUT.
    • Reason: POST is generally used for actions that are not strictly idempotent or where the server controls resource creation/identity (like creating suggestions or submitting votes). PUT implies replacing a resource at a known URI, which doesn't fit the semantics of suggesting new items, submitting change recommendations, or recording votes.
  • Reference Objects (*Reference): Instead of *Reference objects (ID + Name), full related objects are embedded in responses.
    • Reason: Embedding the full related object (e.g., the complete Site object within the Coupon response, the Merchant object within the Site response) achieves the goal of reducing follow-up requests by providing all relevant information directly, offering more data than just ID and name, without needing separate *Reference schema definitions.
  • Coupon Query API Path (/coupons/site/{domain}/list): The endpoint remains /coupons with filtering options like filter_by[site_domain].
    • Reason: Maintaining a single /coupons endpoint with comprehensive filtering (filter_by) provides a more flexible and consistent RESTful interface for searching coupons based on various criteria, rather than creating specialized path structures for common filters like domain.
  • Default Server URL: The syrup URL is included, marked as provider-specific, rather than removing it entirely.
    • Reason: While no single URL should be mandated as the default for all providers, the OpenAPI specification requires at least one server URL for tooling and documentation generation. An example URL serves as a necessary placeholder and illustrates the expected structure.
  • PATCH Semantics: PATCH endpoints for suggestions were replaced with POST /{resource}/{id}/suggestions.
    • Reason: The original use of PATCH for submitting suggestions didn't align well with the HTTP PATCH standard (partial updates). Using dedicated POST endpoints for suggestions clarifies the intent – submitting a recommendation for change rather than directly patching the resource.

Addressed Feedback Points (Summary)

  • Bearer Token JWT: Clarified description.
  • HTTP Status Codes: Added standard reusable responses (400, 401, 403, 404, 406, 429, 500, 501).
  • Basic Auth: Removed.
  • Signup URL: Added optional signup_url to info.
  • Coupon Query API Clarity (Domain): Added filter_by[site_id] and filter_by[site_domain].
  • Site Info Route: Added /sites and /sites/{site_id} endpoints.
  • Pagination Object (allOf): Implemented using reusable PaginationInfo.
  • Coupon Rating/Voting Route: Added POST /coupons/{id}/votes.
  • filterBy Range (?,): Confirmed patterns and description allow this.
  • Category vs. Tag: Added descriptions to clarify the distinction.
  • BOGO Renaming: Renamed to BUY_ONE_GET_ONE_FREE.
  • OTHER Discount Type: Added.
  • Case Consistency: Enforced snake_case.
  • Coupon site Field: Added required site field linking to the Site object.
  • Optional Coupon Description: Made optional.
  • Min/Max Values: Added where applicable.
  • Data Model (Coupon->Site->Merchant): Implemented this structure.
  • Rate Limit Headers: Added to error responses via components.
  • Reusable Components: Extensively refactored to use components.

@ImGajeed76
Copy link
Collaborator Author

@solonovamax what do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants