This project is a personal learning project. OMDB API is a movie and series database presented as a JSON-api. The data is downloaded from OMDB which is a free community driven database for film media. The goal of the project has been to create a full featured ideomatic Go JSON API, which uses few dependencies and abstractions.
Stack:
- HTTPRouter for routing
- PostgreSQL database
- Models built with raw SQL and Go standard libary
- Auth and user management built from cratch
- Mailer implemented with Go-mail and Mailtrap SMTP relay
- Makefile for automations
- Hosting from a Ubuntu VM with Nginx reverse proxy and rsync for file transfer and shell scripts
Sentral to the design of the application is Alex Edwards' books Let's Go and Let's Go Further. You can read more about the technology stack and design desitions taken in the project below.
Created by Torkel Aannestad
Jump to the API-documentation: API documentation
The API is open for anyone to use. Sign up and activate you user account and you are ready to use the API. The database will occationally be reset, but please don't perform to many destructive operations.
You'll get access with 3 steps:
- User signup
- Confirm your email with token
- Authenticate with email and password to get a request token
- Base url: https://omdb-api.torkelaannestad.com
- Endpoint: POST /v1/users
- Body: name, email, password
BODY='{"name": "Jake Perolta","email": "jake.perolta@example.com", "password": "yourSecurePassword"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/usersAn email is sent to your email with activation token. Send the following request to active your account.
- Endpoint: PUT /v1/users/activate
- Body: token
BODY='{"token": token-from-email}'
curl -X PUT -d "$BODY" https://omdb-api.torkelaannestad.com/v1/users/activateReponse: a user object with updated "activated" value.
- Endpoint: POST /v1/auth/authentication
- Body: email, password
BODY='{"email": "yourEmail@example.com", "password": "pa55word"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/auth/authenticationResponse:
{"authentication_token":{"token":"yourTokenHere","expiry":"2024-12-13T09:16:07.9290901Z"}}Request data
- Endpoint: GET /v1/movies/:id
curl -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movies/35819- OMDB is imported from CSV files. An import SQL script is created to set up the data model in a good starting state. See /sql/data-import/run.sql for all the set up steps.
- Added to Makefile to transfer csv data and import to DB in production.
- After initial data import migrations are handled with goose from the sql/schema directory.
- sqlc is configured for autogenerating json tags for Go structs. The generated types are not used directly but copied and modified. This way we get better control over the context.Context instance and error handling. We also get full control when needing to build dynamic queries.
- PostgreSQL configured with citext plugin for user email column to make string case insensitive.
- Full text search features in PostgreSQL is configured to enabled a good search experience with for examample movies or people resources.
- MailTrap for sending transational emails.
- go-mail for handling SMTP.
- Sending email with background Go routine
- Email templates are found in assets/templates
- Authenticate middleware ensures that we retrieve the user from the database or know that the user is anonymous. The user is added to the request context your later use.
- The protectedRoute middleware bounces the user if she is not activated, has the right permission or is anonymous.
- IP based rate limiting with x/time/rate package
- Getting user's IP with Realip package
- github.com/tomasen/realip
- Error triage is implemented in readJSON() helper function to ensure that potensial errors from reading json body is caught and error messages regarding what the issue is can be sendt to the user. A standardized set of response messages are found in cmd/api/errors.go to ensure that only known formulations will reach the end user, and thus hiding for example error messages bubbling from PostgreSQL.
- Improved testing
- More advance auth features like MFA and additional rate limiting on auth endpoints.
Base URL and example endpoint:
https://omdb-api.torkelaannestad.com/v1/healthcheck- To get access to the API please see the quickstart section or the users and auth resources.
- Error handling and expected status codes are found in error handling.
- Permissions. The api is implementet with permission based authorization. Upon signup your user will be granted both read and write access to most resources. Please behave nicely.
- Optimistic concurrency control is applied to any records that can be updated thought the version field. This way multile simultanious requests to update a will fail with status code 409 conflict.
Overview
- Healthcheck
- Movies
- People
- Casts
- Jobs
- Categories
- Movie Keywords
- Movie Categories
- Movie Links
- People Links
- Trailers
- Images
- Users
- Authentication
GET /v1/healthcheck- Description: Check the health status of the API.
- Authentication: None
The movies table include both movies, series and episodes. Series uses the parent_id and series_id fields to form a hierarchy between top level series, seasons and episodes.
- Movie Response example:
{
"id": 1,
"parent_id": null,
"series_id": null,
"name": "Inception",
"date": "2010-07-16T00:00:00Z",
"kind": "movie",
"runtime": 148,
"budget": 160000000,
"revenue": 829895144,
"homepage": "https://www.inceptionmovie.com",
"vote_average": 8.8,
"vote_count": 210000,
"abstract": "A skilled thief is given a chance at redemption if he can successfully perform inception.",
"version": 1
}- Description: Retrieve a list of movies. The endpoint allows full text search thought query parameters.
- Query parameters:
- name: Full text search
- kind: movie, series, season, episode, movieseries (enum)
- page: default 1
- page_size: number of record for each page
- sort: default "id". Use "-" for descending order. Valid values are: "id", "name", "date", "runtime", "-id", "-name", "-date", "-runtime".
- Permission: movies:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/movies?name=dark%20knight&kind=movie&page=1&page_size=2&sort=id"Example response:
{
"metadata": {
"current_page": 1,
"page_size": 2,
"first_page": 1,
"last_page": 3,
"total_records": 5
},
"movies": [
{
"id": 155,
"parent_id": 158459,
"series_id": null,
"name": "The Dark Knight",
"date": "2008-07-18T00:00:00Z",
"kind": "movie",
"runtime": 150,
"budget": 185000000,
"revenue": 970000000,
"homepage": "",
"vote_average": 6.9402985573,
"vote_count": 67,
"abstract": "",
"version": 1
},
{
"id": 32937,
"parent_id": 158459,
"series_id": null,
"name": "The Dark Knight Rises",
"date": "2012-07-20T00:00:00Z",
"kind": "movie",
"runtime": 164,
"budget": 250000000,
"revenue": 576798000,
"homepage": "http://www.thedarkknightrises.com/",
"vote_average": 6.125,
"vote_count": 8,
"abstract": "",
"version": 1
}
]
}- Description: Create a new movie.
- Body: send a movie object as the body of the request. id and version should not be included. Parent_id and series_id are nullable int64 types and can be omited.
- Permission: movies:write
BODY='{"name":"Go programming is awesome","date":"2024-12-02T00:00:00Z", "kind":"movie", "runtime":108,"budget":0,"revenue":0,"homepage":"", "vote_average": 5.4, "votes_count": 23, "abstract": ""}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/moviesResponse
{
"movie": {
"id": 272776,
"parent_id": {
"Int64": 0,
"Valid": false
},
"series_id": {
"Int64": 0,
"Valid": false
},
"name": "Go programming is awesome",
"date": "2024-12-02T00:00:00Z",
"kind": "movie",
"runtime": 108,
"budget": 0,
"revenue": 0,
"homepage": "",
"vote_average": 5.4,
"vote_count": 23,
"abstract": "",
"version": 1
}
}
- Description: Retrieve a specific movie by ID.
- Query Parameter: id is a movie_id
- Permission: movies:read
curl -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movies/35819Response: same as create movie.
- Description: Update a specific movie by ID.
- Query Parameter: id is a movie_id
- Body: fields that you want to update.
- Permission: movies:write
BODY='{ "vote_average": 5.6, "votes_count": 25}'
curl -X PATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movies/272775- Description: Delete a specific movie by ID.
- Query Parameter: id is a movie_id
- Permission: movies:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movies/272775- Description: Retrieve a list of people. The endpoint supports full-text search using query parameters.
- Query Parameters:
- name: Full text search by name.
- page: Page number for pagination, default is 1.
- page_size: Number of records for each page.
- sort: Default is "id". Use "-" for descending order. Valid values are: id, name, birthday, -id, -name, -birthday.
- Permission: people:read
- Gender: 0=male, 1=female, 2=non-binary, 99=not spesified
curl -H "Authorization: Bearer YOUR_TOKEN" "https://omdb-api.torkelaannestad.com/v1/people?name=john&page=1&page_size=5&sort=id"- Description: Create a new person.
- Body: Send a person object as the body of the request. id and version should not be included.
- Permission: people:write
BODY='{"name":"John Doe","birthday":"1944-05-14T00:00:00Z","gender":"0","aliases":["foo, bar"]}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/people- Description: Retrieve a specific person by ID.
- Query Parameter: person id
- Permission: people: read
curl -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/people/311418- Description: Update a specific person by ID.
- Path Parameter: id is the person_id.
- Body: Fields that you want to update.
- Permission: people:write
BODY='{"name":"John Doeski","gender":"99"'
curl -X PPATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/peopleResponse: the updated user object.
- Description: Delete a specific person by ID.
- Query Parameter: person id.
- Permission: people:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/people/311418- Description: Create a new cast entry.
- Body: see field below. Position field is the position that the cast will appear in the list. Meaning position 1 should be example be lead actor.
- Permission: casts:write
BODY='{"movie_id":35819,"person_id":287,"job_id":15,"role":"Very cool role","position":1}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/casts- Description: Retrieve casts associated with a specific movie by movie ID.
- Query Parameter: movie id.
- Permission: casts:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/casts/by-movie-id/35819"Reponse: list of casts
- Description: Retrieve casts associated with a specific person by person ID.
- Query Parameter: person id.
- Permission: casts:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/casts/by-person-id/2524"Reponse: list of casts
- Description: Update a specific cast entry by ID.
- Query Parameter: cast id.
- Body: Fields that you want to update.
- Permission: casts:write
BODY='{"role":"Luke Skywalker","position":2}'
curl -X PATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/casts/1260480- Description: Delete a specific cast entry by ID.
- Query Parameter: cast id.
- Permission: casts:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/casts/1260479- Description: Create a new job. Body: name of the job
- Permission: jobs:write
BODY='{"name":"Executive Producer"}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/jobs- Description: Retrieve a specific job by ID.
- Query Parameter: job id.
- Permission: jobs:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/jobs/1050"- Description: Update a specific job by ID.
- Query Parameter: job id.
- Body: fields you want do update.
- Permission: jobs:write
BODY='{"name":"Super Executive Producer"}'
curl -X PATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/jobs/1050- Description: Delete a specific job by ID.
- Query Parameter: job id.
- Permission: jobs:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/jobs/1050- Description: Create a new category.
- Body: name of the category, parent_id, root_id are nullable int64 types. Use parent and root id to create sub-categories.
- Permission: categories:write
BODY='{"name":"Family Drama"}'
BODY='{"name":"Family Drama", "parent_id":12,"root_id":1}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/categories- Description: Retrieve a specific category by ID.
- Query Parameter: category id.
- Permission: categories:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/categories/20705"- Description: Update a specific category by ID.
- Query Parameter: category id.
- Body: fields you want do update.
- Permission: categories:write
BODY='{"name":"Genre Adventure Subcategory", "parent_id":12,"root_id":1}'
curl -X PATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/categories/20705"- Description: Delete a specific category by ID.
- Query Parameter: category id.
- Permission: categories:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/categories/20705- Description: Add keywords to a movie.
- Body: movie_id and category_id
- Permission: category-items:write
BODY='{"movie_id":35819,"category_id": 10}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-keywords- Description: Retrieve keywords associated with a movie by movie ID.
- Query Parameter: movie_keyword id.
- Permission: category-items:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/movie-keywords/35819"- Description: Delete keywords from a movie.
- Body: movie_id and category_id
- Permission: category-items:write
BODY='{"movie_id":35819,"category_id":10}'
curl -X DELETE -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-keywords- Description: Add categories to a movie.
- Body: movie_id and category_id
- Permission: category-items:write
BODY='{"movie_id":35819,"category_id": 10}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-categories- Description: Retrieve categories associated with a movie by movie ID.
- Query Parameter: movie_category id.
- Permission: category-items:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/movie-categories/35819"- Description: Delete categories from a movie.
- Body: movie_id and category_id
- Permission: category-items:write
BODY='{"movie_id":35819,"category_id":10}'
curl -X DELETE -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-categories- Description: Create a movie link.
- Body:
- source: query parameter or id for the source.
- key: must be: "wikidata", "wikipedia", "imdbperson"
- language: english is the only language used. When using xx as value english is assumed.
- Permission: movie-links:write
BODY='{"source":"Fight_Club","key":"wikipedia","movie_id":550,"language":"xx"}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-links- Description: Retrieve links associated with a movie by movie ID.
- Query Parameter: movie id.
- Permission: movie-links:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/movie-links/550"- Description: Delete a movie link by ID.
- Query Parameter: movie-link id.
- Permission: movie-links:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/movie-links/348425- Description: Create a people link.
- Body:
- source: query parameter or id for the source.
- key: must be: "wikidata", "wikipedia", "imdbperson"
- language: english is the only language used. When using xx as value english is assumed.
- Permission: people-links:write
BODY='{"source":"Brad_Pitt","key":"wikipedia","person_id":287,"language":"xx"}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/people-links- Description: Retrieve links associated with a person by person ID.
- Query Parameter: person link id.
- Permission: people-links:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/people-links/287"- Description: Delete a people link by ID.
- Query Parameter: person-link id.
- Permission: people-links:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/people-links/213048- Description: Add a trailer to a movie.
- Body:
- key: id of the yotube or vimeo video
- movie_id
- language
- source: "youtube" or "vimeo"
- Permission: trailers:write
BODY='{"key":"youtube-id-from-url","movie_id":35819,"language":"en","source":"youtube"}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/trailers- Description: Retrieve trailers associated with a movie by movie ID.
- Query Parameter: trailer id.
- Permission: trailers:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/trailers/35819"- Description: Delete a trailer by ID.
- Query Parameter: trailer id.
- Permission: trailers:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/trailers/10922- Description: Upload an image.
- Body:
- object_id: id of person, movie, job, category
- object_type: "Movie", "Person", "Job", "Category"
- Permission: images:write
BODY='{"object_id":272775, "object_type": "Movie"}'
curl -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/images- Description: Retrieve a specific image by ID.
- Query Parameter: image id.
- Permission: images:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/images/60360"- Description: Retrieve images by object ID.
- Query Parameter:
- object_id: id of person, movie, job, category
- object_type: "Movie", "Person", "Job", "Category"
- Permission: images:read
curl -H "Authorization: Bearer yourTokenHere" "https://omdb-api.torkelaannestad.com/v1/images?object_id=272775&object_type=Movie"- Description: Update a specific image by ID.
- Body:
- object_id: id of person, movie, job, category
- object_type: "Movie", "Person", "Job", "Category"
- Permission: images:write
BODY='{"object_id":272775, "object_type": "Movie"}'
curl -X PATCH -d "$BODY" -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/images/60360- Description: Delete a specific image by ID.
- Query Parameter: image id.
- Permission: images:write
curl -X DELETE -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/images/60360- Description: Register a new user.
- Authentication: None
BODY='{"name": "Jake Perolta","email": "jake.perolta@example.com", "password": "yourSecurePassword"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/users- Description: Activate a registered user.
- Body: token from email
- Authentication: None
BODY='{"token": token-from-email}'
curl -X PUT -d "$BODY" https://omdb-api.torkelaannestad.com/v1/users/activate- Description: Resend activate token to users email.
- Body: email
- Authentication: None
BODY='{"email": yourEmail@example.com}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/users/resend-activation-tokenResponse: json message confirmation.
Authentication endpoint are protected with an additional rate limiter with a slow refill rate. This is to protect agains password sprays or other brute force methods agains the auth resources.
- Description: Authenticate a user and obtain a token.
- Body: email, password
- Authentication: None
BODY='{"email": "yourEmail@example.com", "password": "pa55word"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/auth/authenticationResponse: auth token
- Description: Change user password and obtain a token. Auth token is regenerated and old sessions are invalidated.
- Body: current_password and new_password
- Authenticated
BODY='{"current_password": "pa55word", "password": "NewStrongerpa55word"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/auth/change-passordResponse: auth token
- Description: Reset user password. Send user email in body and receive verification code by email.
- Body: email
BODY='{"email": "yourEmail@example.com"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/auth/password-reset-verify-emailResponse: json message
- Description: Reset user password. Send email verification token from email in body along with new password.
- Body: token
BODY='{"token": "ZAAKSYTB2CV2RU2OOQ2JA5K35Y", "new_password": "NewStrongerpa55word"}'
curl -d "$BODY" https://omdb-api.torkelaannestad.com/v1/auth/reset-passwordResponse: new authentication token
- Description: Revokes all sessions for user.
- Authentication: True
curl -X POST -H "Authorization: Bearer yourTokenHere" https://omdb-api.torkelaannestad.com/v1/auth/revokeResponse: json message confirmation
- 400 Bad Request: General response when body or query params are invalid.
- 422 Unprocessable Entity: Failed validations response. Fields are errors are specified.
- 404 Not Found: If a movie with the specified ID does not exist.
- 409 Conflict: If there's a concurrency edit conflict (version mismatch).
- 405 Method Not Allowed: If the method is not allowed on the specified route.
- 429 Too Many Request: Failed due to rate limiting.
- 401 Unauthorized: any issue with auth token.
- 403 Forbidden: When trying to access resources without respective permission or account not active.
- 500 Server error: General server error response