Skip to main content
important

This is a contributors guide and NOT a user guide. Please visit these docs if you are using or evaluating SuperTokens.

API Design

info

This applies to CDI and FDI specs.

Section 1) Common rules between CDI and FDI#

  • 1) All input / output will be of type application/json

  • 2) There are two types of errors in an API:

    • a) Expected errors: These are errors like a user trying to sign in with wrong credentials, or an existing email trying to sign up. Essentially, any error that comprises of a logical, but expected error comes in here.

      These errors will return a http status code 200.

    • b) Unexpected errors: These are errors like API not found (404), or the server / db going down (500), or the API key supplied (to the core) is incorrect / missing (401).

      These errors yield a non 200 response.

    Bad input errors (400 status code) can be tricky - if the input is bad because of a user's input and creating a custom error might help them fix the input, then we would make that an expected error, else we would use 400. For example, if an API requires a userId input, and that is missing, then we can use 400 error (unexpected input) since that will happen due to a coding error. However, if an API requires a value which must be within a certain range, and that value is provided by the user, then having a specific error type for that may be better.

  • 3) Due to above rule, the response from each API has a "status" field in it and it only applies to responses with status code 200.

    If everything goes as expected in the API, the value of status is "OK" - like {status: "OK"}.

    On the other hand, if there is an expected error, then the value should reflect that - for example: {status: "EMAIL_ALREADY_EXISTS_ERROR} is returned when someone tries to sign up with an exisitng email.

    Notice that in case of expected errors, the status value ends with _ERROR.

  • 4) We should try and minimise the number of expected error types across all recipes.

  • 5) Allowed HTTP methods are GET, POST, PUT. We expect a body for all except for GET. For POST, PUT, we ignore query params.

    We do not allow DELETE since it's unclear if the request params should be in the body or as query params. Instead, we use */*/remove POST. For example, if we are creating a delete user API, it will be */user/remove POST.

  • 6) Recipes can have the same path. We can know which recipe a request is targeting by reading the request's rid header. For example, if the request if for the session recipe, then the value of the rid header for that recipe will be session.

important

If adding a new API in swagger, and the path is the same as another API, you can add an empty character (this: ) to the end of the path to make it different, yet not visible to the user.

  • 7) Sometimes input or output to an API may require multiple types. Here, we design the API such that there is a common field in the input / output (across all types) which tells the API / consumer what type it is. For example, we can have an output type as:

    {    status: "OK",    type: "emailpassword",    email: string,    userId: string} | {    status: "OK",    type: "thirdparty",    email: string,    userId: string,    thirdPartyInfo: {        id: string, userId: string    }}

    As you can see, the consumer of the above can know which type it is based on the type field in the object.

  • 8) The path should reflect clearly what the intended cause of the API is. For example, if we are deleting a single user, the path can be */user/remove POST. However, for deleting multiple (including just one) users, the path should be */users/remove POST

  • 9) We have to try and minimise the number of APIs. For example, if there is a way to get a user's info via email or userId, we don't need to have two APIs for this, but one API like */user GET which takes either the email (*/user?email=...) or the userId (*/user?userId=...).

  • 10) An API can have custom headers other than what's specified in this doc. For example, the session related APIs make use of cookies and a couple of other custom header values to work. We want to minimise this of course, as it complicates things.

  • 11) We are not allowed to have variables in the path. So for example, we do not do /user/:id GET where :id is the user ID of the user whose info we want to get. Instead, the id would come as a query param: /user?id=... GET. The reason for this is that it adds additional complexity to how our SDKs / core would integrate with the respective web frameworks.

  • 12) All paths should be small case. No camelcase and no underscores. So words like /phoneNumber/ in a path should become /phonenumber/

Section 2) Rules specific to CDI#

  • 1) There will be an input header called cdi-version. This can be used by the coreto run different logic based on the CDI versions it supports. This is compulsory.

  • 2) There may be an api-key header value in case the user has set an API key. This is required for most APIs in case the user has set it (in the core), else the core should throw 401 error. On the other hand, if the user has not set it (in the core), an API that expects the api-key header should still run even if there is no such header in the request.

  • 3) All APIs that are specific to a recipe should be prefixed with /recipe/*. Others that are common across all recipes (like get user count, or delete a user), should not have any such prefix.

  • 4) Any operations thatpermanently delete objects are allowed, since this is only queried by the backend layer of a user.

Section 3) Rules specific to FDI#

  • 1) There might be an input header called fdi-version. This can be used by the backend SDK to run different logic based on the FDI versions it supports. This is optional.

  • 2) The prefix for these APIs is the apiBasePath value that the user has defined in their config. By default, it is /auth/

  • 3) Any operations that permanently delete objects are NOT allowed, since these APIs can be queried by the frontend (hence anyone) - excepting if the APIs are authenticated in some way (for example by an active session).

  • 4) All APIs must have a GENERIC_ERROR response so that users show a UI message for their custom errors.

Which frontend SDK do you use?
supertokens-web-js / mobile
supertokens-auth-react