The News REST API server.
- Asynchronous logger
- Servant-like type-level routing implementation
- Using TH in favor of Type Driven Development
- Effect tracking system
- As a result - testable code and extensibility
Clone current repository:
git clone git@github.com:krendelhoff/news-server.git
cd news-server
Create and start NixOS container provided by flake.nix
:
nixos-container create news-server --flake .#nixos-container
nixos-container start news-server
Then you should be able to ping news-server service:
$ curl <container_ip>:7980
{"messages":["Hello, World!"]}
Done! Service is ready to use.
You can also tweak configuration parameters at service declaration.
All available configuration options listed here.
To run tests, use can use:
nix run .#"server:test:server-test"
All migrations are located at server/migrations/
folder.
There are four levels of logging verbosity:
- Error - critical errors that lead to program or request handling halting
- Warn - recoverable errors
- Info - notifications about current actions
- Debug - debug information
CURL scripts are located at scripts/
folder.
-
server-lib
src/
DB.hs
- contains DB related helper functionsErrors.hs
- contains server error definitionsRouter.hs
- contains type-level routing implementationInfrastructure.hs
- namespace with all necessary server-lib functionsUtils.hs
- contains project utility functions.Types/
TH.hs
- contains Template Haskell macros for specialized type creationRouter.hs
- contains router related typesDB.hs
- contains DB related typesLenses.hs
- contains some lens helpersInfrastructure
- namespace with all necessary server-lib typesTH/
Classes.hs
- contains Template Haskell macros for typeclass definition and instances creation
-
server
src/
Utils.hs
- contains some utility functionsMigration.hs
- contains applyMigrations functionLogger.hs
- contains asynchronous logger implementationEffects.hs
- namespace with all effect typeclassesEffects/
- contains entity-specific effects definitions using typeclasses<entity>.hs
Server.hs
- contains top level server API definitionServer/
- contains API method implementationsAuth.hs
- contains implementation of authentication and "/auth" APIErrors.hs
- contains API method errorsUser.hs
- contains API definition for usersUser/
- contains API for users implementation<entity>.hs
Admin.hs
- contains API definitionsAdmin/
- contains API for admins implementation<entity>.hs
Database/
- contains entity-specific database functions<entity>.hs
Types/
Auth.hs
- contains types used for authentificationEnvironment.hs
- contains config, environment and base monad stack definitionsLogger.hs
- contains logger implementation-related typesUtils.hs
- contains types for utility functions<entity>.hs
- contains entity-specific type definitions
Request examples may be found at folder scripts/
.
For auth endpoints:
Code | Description |
---|---|
400 |
Bad request: can't parse request body, present query parameter or capture id |
404 |
Not found |
For User
protected endpoints:
Code | Description |
---|---|
400 |
Bad request: can't parse request body, present query parameter or capture id |
401 |
No authorization header, no such token present or token violates format |
403 |
Token expired or not enough rights |
404 |
Not found |
For Admin
protected endpoints:
Code | Description |
---|---|
404 |
In case of any authorization errors |
400 |
Bad request: can't parse request body, capture id or present query parameter (if authorized) |
Every response with error code is accompanied by explanatory messages.
Permission: None
Endpoint for user creation.
Request body implements an interface:
interface ICreateUserRequest {
name: string;
surname: string;
login: string;
avatar: string?; // ID of the picture in database
password: string;
}
Response body implements an interface:
interface ICreateUserResponse {
token: string;
refreshToken: string;
expires: timestamptz;
}
Code | Description |
---|---|
200 |
Created successfully |
404 |
Picture does not exist |
Permission: None
Endpoint for token retrieval.
Request body implements an interface:
interface ILoginRequest {
login: string;
password: string;
}
Response body implements an interface:
interface ILoginResponse {
token: string;
refreshToken: string;
expires: timestamptz;
}
Code | Description |
---|---|
200 |
Successful login |
404 |
User not found |
Permission: User
Authorization header must contain refresh token.
Response body implements an interface:
interface IRefreshResponse {
token: string;
refreshToken: string;
token: string;
}
Code | Description |
---|---|
200 |
Token refreshed successfully |
Permission: User
Endpoint for getting current authenticated user.
Response body implements an interface:
interface IUserPayloadResponse {
userId: string;
name: string;
surname: timestamptz;
login: string;
avatar?: string;
createdAt: timestamptz;
privileded: bool;
}
Code | Description |
---|---|
200 |
Returns user payload successfully |
Permission: Admin
Endpoint for deleting users. Returns empty response body with the following codes:
Code | Description |
---|---|
204 |
Removed successfully |
404 |
User not found |
403 |
Not enough rights (attempt to delete an admin) |
Warning: Deleting user also deletes all entities related to deleted user.
Permission: User
Endpoint for posting picture to server. Accepts raw bytes request body with corresponding content-type header.
interface IPicturePayloadResponse {
pictureId: string;
}
Code | Description |
---|---|
200 |
Picture uploaded successfully |
Permission User
Endpoint for getting category information. Method accepts query parameters:
- recursive: bool, default true
Response body implements an interface:
interface IGetCategoryResponse {
categoryId: string;
title: string;
parent?: string;
}
With recursive=true, response body implements an interface: Response body implements an interface:
interface IGetCategoryResponse {
categoryId: string;
title: string;
parent?: IGetCategoryResponse;
}
Code | Description |
---|---|
200 |
Returns category payload successfully |
404 |
Category not found |
Permission User
Endpoint for category creation. Omitted parent field means root category becomes parent.
Request body implements an interface:
interface ICreateCategoryRequest {
title: string;
parent?: string;
}
Response body implements an interface:
interface ICreateCategoryResponse {
categoryId: string;
title: string;
parent?: string;
}
Code | Description |
---|---|
200 |
Returns category payload successfully |
406 |
Title is not unique error |
Permission Admin
Endpoint for deleting categories. Returns empty response body with the following codes:
Code | Description |
---|---|
200 |
Removed successfully |
403 |
Attempt to remove root category |
404 |
Category not found |
Permission Admin
Endpoint for updating categories.
Method accepts query parameters:
- title: string
- parent: string (parent category ID)
Response body implements an interface:
interface IUpdateCategoryResponse {
categoryId: string;
title: string;
parent?: string;
}
Code | Description |
---|---|
200 |
Updated successfully |
404 |
Category not found |
404 |
Rebase destination not found |
406 |
Rebase destination incorrect (forms a cycle) |
406 |
Title is not unique |
Permission Admin
Response body implements an interface:
interface IGetAuthorResponse {
userId: string;
description: string;
}
Code | Description |
---|---|
200 |
Returns author payload successfully |
404 |
Author not found |
Permission Admin
Request body implements an interface:
interface IPromoteUserRequest {
userId: string;
description: string;
}
Response body implements an interface:
interface IPromoteUserResponse {
userId: string;
description: string;
}
Code | Description |
---|---|
200 |
Returns author payload |
404 |
User not found |
406 |
Current user is already an author |
Permission Admin
Request body implements an interface:
interface IUpdateAuthorRequest {
description: string;
}
Response body implements an interface:
interface IUpdateAuthorResponse {
userId: string;
description: string;
}
Code | Description |
---|---|
200 |
Returns updated author payload |
404 |
Author not found |
Permission Admin
Endpoint for downgrading author to regular user.
Code | Description |
---|---|
204 |
Downgraded author successfully |
404 |
Author not found |