REST API
Representational State Transfer (REST) is an architectural style for exposing and consuming resources over HTTP. Following well-considered conventions keeps such APIs consistent, maintainable, and pleasant to work with.
Terminology
Section titled “Terminology”All terminology follows the Effect HTTP API Reference and the OpenAPI 3.1.0 Specification. Where the two sources clash or fall short, the following table provides clarity — settling naming conflicts and introducing new terms.
| Resource |
|---|
| An abstraction representing a domain concept — either a single item or a collection. |
API Design
Section titled “API Design”Designing an API means defining the contract of every resource interaction and organizing those contracts into an API Definition, API Groups, and API Endpoints. Each API Endpoint is fulfilled by an API Handler as a separate concern — owning the logic but not the contract.
API Definition (1)└── API Group (1..N) └── API Endpoint (1..N) └ ─ ─ API Handler (1)API Definition
Section titled “API Definition”The API Definition serves as the source of truth, drives the generated OpenAPI specification, and enforces type safety across all API Handlers.
Create an API Definition file:
Directoryapplications
Directorygateway
Directorysrc
Directoryapi
- api.definition.ts
- …
- …
Scaffold it based on the following snippet:
import { HttpApi } from 'effect/unstable/httpapi';export const ApiDefinition = HttpApi.make('Api');
API Group
Section titled “API Group”The API Group gathers API Endpoints that operate on related resources, keeping their contracts defined in one place.
Locate the API Definition file:
Directoryapplications
Directorygateway
Directorysrc
Directoryapi
- api.definition.ts
- …
- …
Add an API Group based on the following snippet:
import { HttpApi, HttpApiGroup } from 'effect/unstable/httpapi';const ApiGroupPlayers = HttpApiGroup.make('Players');export const ApiDefinition = HttpApi.make('Api').add(ApiGroupPlayers);
API Endpoint
Section titled “API Endpoint”The API Endpoint defines the contract for a specific interaction with a resource.
Locate the API Definition file:
Directoryapplications
Directorygateway
Directorysrc
Directoryapi
- api.definition.ts
- …
- …
Add an API Endpoint based on the following snippet:
import {HttpApi,HttpApiEndpoint,HttpApiGroup,} from 'effect/unstable/httpapi';const ApiGroupPlayers = HttpApiGroup.make('Players').add(HttpApiEndpoint.post('create-player', '/players'),HttpApiEndpoint.get('get-players', '/players'),HttpApiEndpoint.get('get-player', '/players/:id'),HttpApiEndpoint.patch('update-player', '/players/:id'),HttpApiEndpoint.put('replace-player', '/players/:id'),HttpApiEndpoint.delete('delete-player', '/players/:id'),);export const ApiDefinition = HttpApi.make('Api').add(ApiGroupPlayers);Extend the API Endpoint based on the following snippet:
import {CreatePlayerError,CreatePlayerPayload,CreatePlayerRequestHeaders,CreatePlayerSuccess,} from '@applications/gateway/src/api/players/create-player.schema.ts';import {DeletePlayerError,DeletePlayerParameters,DeletePlayerRequestHeaders,DeletePlayerSuccess,} from '@applications/gateway/src/api/players/delete-player.schema.ts';import {GetPlayerError,GetPlayerParameters,GetPlayerRequestHeaders,GetPlayerSuccess,} from '@applications/gateway/src/api/players/get-player.schema.ts';import {GetPlayersError,GetPlayersQuery,GetPlayersRequestHeaders,GetPlayersSuccess,} from '@applications/gateway/src/api/players/get-players.schema.ts';import {ReplacePlayerError,ReplacePlayerParameters,ReplacePlayerPayload,ReplacePlayerRequestHeaders,ReplacePlayerSuccess,} from '@applications/gateway/src/api/players/replace-player.schema.ts';import {UpdatePlayerError,UpdatePlayerParameters,UpdatePlayerPayload,UpdatePlayerRequestHeaders,UpdatePlayerSuccess,} from '@applications/gateway/src/api/players/update-player.schema.ts';import {HttpApi,HttpApiEndpoint,HttpApiGroup,} from 'effect/unstable/httpapi';const ApiGroupPlayers = HttpApiGroup.make('Players').add(HttpApiEndpoint.post('create-player', '/players', {headers: CreatePlayerRequestHeaders,payload: CreatePlayerPayload,success: CreatePlayerSuccess,error: CreatePlayerError,}),HttpApiEndpoint.get('get-players', '/players', {headers: GetPlayersRequestHeaders,query: GetPlayersQuery,success: GetPlayersSuccess,error: GetPlayersError,}),HttpApiEndpoint.get('get-player', '/players/:id', {headers: GetPlayerRequestHeaders,params: GetPlayerParameters,success: GetPlayerSuccess,error: GetPlayerError,}),HttpApiEndpoint.patch('update-player', '/players/:id', {headers: UpdatePlayerRequestHeaders,params: UpdatePlayerParameters,payload: UpdatePlayerPayload,success: UpdatePlayerSuccess,error: UpdatePlayerError,}),HttpApiEndpoint.put('replace-player', '/players/:id', {headers: ReplacePlayerRequestHeaders,params: ReplacePlayerParameters,payload: ReplacePlayerPayload,success: ReplacePlayerSuccess,error: ReplacePlayerError,}),HttpApiEndpoint.delete('delete-player', '/players/:id', {headers: DeletePlayerRequestHeaders,params: DeletePlayerParameters,success: DeletePlayerSuccess,error: DeletePlayerError,}),);export const ApiDefinition = HttpApi.make('Api').add(ApiGroupPlayers);
API Handler
Section titled “API Handler”The API Handler fulfills a single API Endpoint. It holds the actual logic — receiving the declared input and producing the declared output.