Skip to main content

API application

API application implements the API for the Client and Provider applications, as well as integration with them. The application does not have its own database and uses QuickBlox to store and share data. Within this application, an API has been implemented that extends the capabilities of QuickBlox, adapting them to the features of QConsultation. However, all other QuickBlox methods can be used without modification.

Prerequisites

Structure

apps
└──── api
├──── node_modules # npm packages used by the package
├──── dist # build of the package
├──── src # business logic of the package split into subfolders per API
│ ├──── constants
│ ├──── models
│ ├──── plugins
│ ├──── routes
│ ├──── services
│ ├──── types
│ ├──── utils
│ └ app.ts

├ .eslintignore
├ .eslintrc
├ package.json
└ tsconfig.json

Plugins

Plugins define behavior that is common to all the routes in your application. Authentication, caching, templates, and all the other cross cutting concerns should be handled by plugins placed in this folder.

Files in src/plugins folder are typically defined through the fastify-plugin module, making them non-encapsulated. They can define decorators and set hooks that will then be used in the rest of your application.

Check out:

Connected plugins:

  • cors - enables the use of CORS.

  • env - fastify plugin to check environment variables.

  • multipart - plugin to parse the multipart content-type.

  • sensible - plugin adds some useful utilities to your Fastify instance.

  • swagger - automatically generated from your route schemas, or from an existing OpenAPI schema

  • typebox - set TypeBox validator compiler

  • auth - implements a decorator for getting a token.

  • error - plugin to parse errors.

  • quickblox - produces QuickBlox SDK initialization and provide authentication strategies for different roles of users.

    The fastify.verify performs verification of the authorization strategy. Each strategy checks the validity of the token in an Authorization HTTP header.

    Implemented the following strategies:

    • BearerToken - Checks the validity of BEARER_TOKEN from the application configuration. This strategy executes requests on behalf of the owner of the QuickBlox account.
    • ProviderSessionToken - checks the validity of the provider's session token.
    • ClientSessionToken - checks the validity of the client's session token.
    • SessionToken - checks the validity of the session token of the provider or client.

    An endpoint can use multiple authorization strategies.

    In the following example, you will find a very simple implementation that should help you understand how to use this module:

    fastify.get(
    '/',
    {
    onRequest: fastify.verify(
    fastify.BearerToken,
    fastify.ProviderSessionToken,
    ),
    },
    async (request, reply) => {
    /* handler */
    },
    )

Schemas & Models

Fastify uses a schema-based approach, and even if it is not mandatory we recommend using TypeBox to validate your routes and serialize your outputs. Internally, Fastify compiles the schema into a highly performant function.

The supported validations are:

  • body: validates the body of the request if it is a POST, PUT, or PATCH method.
  • querystring: validates the query string.
  • params: validates the route params.
  • headers: validates the request headers.

In addition, the scheme supports other properties:

  • tags - allows grouping endpoints by tag
  • description - description of the endpoint
  • consumes - specifies the MIME Types for the request body.
    • Default: ["application/json"].
    • To upload files: ["multipart/form-data"].
  • security - indicates the authorization method. This schema parameter must be used in conjunction with the definition of an authorization strategy.
    • { apiKey: [] } - matches the BearerToken strategy
    • { providerSession: [] } - matches the ProviderSessionToken strategy
    • { clientSession: [] } - matches the ClientSessionToken strategy

Models (src/models) are generic TypeBox schemas that are used to validate requests and responses.

Example:

// Model from models folder
const UserModal = Type.Object(
{
id: Type.Integer(),
full_name: Type.String(),
email: Type.String({ format: 'email' }),
created_at: Type.String({ format: 'date-time' }),
updated_at: Type.String({ format: 'date-time' }),
},
{ $id: 'QBUser' },
)

// Schema for endpoint
const updateUserSchema = {
tags: ['Users'],
description: 'Update user by id',
params: Type.Object({
id: Type.Integer(),
}),
body: Type.Object({
full_name: Type.String(),
email: Type.String({ format: 'email' }),
}),
response: {
200: Type.Ref(UserModal),
},
security: [{ apiKey: [] }] as Security,
}

// ...

Services

A service is a set of functions that implement business logic for a particular entity.

src/services directory implements functionality for working with OpenAI and QuickBlox entities.

tip

Read more information on OpenAI integration in the OpenAI section.

Routing

Routes define endpoints within your application.

In src/routes folder you should define all the routes that define the endpoints of your web application. Each service is a Fastify plugin, it is encapsulated (it can have its own independent plugins) and it is typically stored in a file; be careful to group your routes logically, e.g. all /users routes in a users.js file.

Folders prefixed with _ will be turned into route parameters.

If you want to use mixed route parameters use a double underscore __.

├── routes
├── __country-__language
│ │ └── actions.ts
│ └── users
│ ├── _id
│ │ └── actions.ts
│ ├── __country-__language
│ │ └── actions.ts
│ └── index.ts
└── app.ts
#
# routes/users/_id/actions.js will be loaded with prefix /users/:id
# routes/__country-__language/actions.js will be loaded with prefix /:country-:language

# curl http://localhost:3000/users/index
# { userIndex: [ { id: 7, username: 'example' } ] }

# curl http://localhost:3000/users/7/details
# { user: { id: 7, username: 'example' } }

# curl http://localhost:3000/be-nl
# { country: 'be', language: 'nl' }

If a single file become too large, create a folder and add a index.js file there: this file must be a Fastify plugin, and it will be loaded automatically by the application. You can now add as many files as you want inside that folder. In this way you can create complex routes within a single monolith, and eventually extract them.

If you need to share functionality between routes, place that functionality into the plugins folder, and share it via decorators.

Endpoints

All information on API endpoints can be found on the API Server page. There you can download the OpenAPI specification and see the description of all available endpoints.

tip

When studying api endpoints, pay attention to Authorization methods, MIME Type and fields of the request body.

The Authorization section specifies the authorization method. Each of the methods indicates which token should be passed in an Authorization HTTP header. Authorization header must be in the format Bearer <token>.

There are 3 authorization methods available:

  • apiKey - BEARER_TOKEN set in app config. Used for API integration.
  • providerSession - provider session token.
  • clientSession - client session token.

The Request section will specify the MIME Type of the request body:

  • application/json - used to send json in the body of the request. Use header: "Content-Type": "application/json".
  • multipart/form-data - used to send files in the body request. Use FormData in JavaScript.