Prototype of backend service for a job board platform. The service provides endpoints for managing job offers with related information, mainly company data.
The service utilizes Spring Data JPA for retrieving data and Spring Web for presenting it over endpoints. The data is cached thanks to Caffeine. The API is globally rate limited using bucket4j library, requests are authorized using OAuth2 JWT with help of Spring Security. API documentation is automatically generated using openAPI and can be accessed via the Swagger UI.
You can find the docker image of the whole application here.
- Retrieval of job offers with various levels of detail and filtering options
- Global rate limiting
- Caching layer
- Authorization and authentication using JWT
Data stored in a PostgreSQL can be retrieved using various endpoints. All input is validated using Hibernate validator, and the data is returned in a JSON format. The data can be filtered, sorted and paginated. Most frequently used queries are cached using Caffeine, which is invalidated after a certain period of time.
Rate limiting is implemented using bucket4j. The limit is globally applied to all requests and refuses handling requests over the limit. The limit can be set through environment variables.
User can register and log in using OAuth 2.0 Resource Server, that is running alongside the application. The user is authenticated using JWT tokens. Unauthorized users can only view data, while authenticated users have different privileges based on their role. Passwords stored in the database are hashed. The tokens are encrypted and signed using RSA keys.
Two filters are implement in the application - one for rate limiting and one for logging request and response details.
Information about the application can be accessed via Spring Actuator. Access to these endpoints is restricted to administrator and superuser accounts.
Basic logging of errors and events is implemented using Logback via the slf4j facade. The logger logs human-readable
statements to the console and JSON statements to a file. The file is rolled over daily or when it reaches a certain
size. Archived logs are kept for a month and then deleted.
Sensitive data is encrypted and never stored explicitly.
A single aspect is used to log the execution time of all service methods dealing with data.
The application can be run using included docker-compose.yaml
. It will be available under the localhost:8080
address. For running the application securely, default credentials and keys should be changed. Full documentation is
created by OpenApi and available under the /swagger-ui.html
or /v3/api-docs
.
The following endpoints will be available:
Endpoint | Method | Description |
---|---|---|
/api/v1/technologies/{id} |
PUT | Update a technology |
/api/v1/job-offers/{id} |
GET | Get a job offer by id |
/api/v1/job-offers/{id} |
PUT | Update a job offer |
/api/v1/job-offers/{id} |
DELETE | Delete a job offer |
/api/v1/companies/{id} |
PUT | Update a company |
/api/v1/companies/{id} |
DELETE | Delete a company |
/api/v1/technologies |
GET | Get all technologies |
/api/v1/technologies |
POST | Add a technology |
/api/v1/job-offers |
GET | Get all job offers |
/api/v1/job-offers |
POST | Add a new job offer |
/api/v1/companies |
GET | Get company by id |
/api/v1/companies |
POST | Add a new company |
/api/v1/auth/user |
POST | Get user details |
/api/v1/auth/register |
POST | Register a new user |
/api/v1/auth/login |
POST | Login as a user |
/api/v1/job-offers/detailed |
GET | Get all job offers projected to detailed view |
/api/v1/job-offers/detailed/{id} |
GET | Get a job offer by id projected to detailed view |
/api/v1/job-offers/criteria |
GET | Get job offers filtered by criteria |
/api/v1/job-offers/criteria-paged |
GET | Get job offers filtered by criteria, paged and sorted |
/api/v1/job-offers/criteria-detailed |
GET | Get job offers filtered by criteria projected to detailed view |
/api/v1/job-offers/criteria-detailed-paged |
GET | Get job offers filtered by criteria, paged and sorted, projected to detailed view |
/api/v1/job-offers/basic |
GET | Get all job offers projected to basic view |
/api/v1/job-offers/basic/{id} |
GET | Get a job offer by id projected to basic view |
/api/v1/addresses |
GET | Get all addresses |
/api/v1/addresses/companies/{companyName} |
GET | Get all addresses by company name |
This table was generated from openAPI docs using this script
The following properties can be set from docker-compose.yaml
:
Environment variable | Default value | Corresponding property name | Description |
---|---|---|---|
JDBC_DATABASE_URL |
jdbc:postgresql://localhost:5432/ | spring.datasource.url |
Database url with hostname and port |
JDBC_DATABASE_NAME |
jobboard | spring.datasource.url |
Name of the database |
JDBC_DATABASE_USERNAME |
api | spring.datasource.username |
Database user username |
JDBC_DATABASE_PASSWORD |
apipassword | spring.datasource.password |
Database user password |
RATE_LIMIT_BUCKET_CAPACITY |
100 | rate.limit.bucket.capacity |
Global bucket capacity |
RATE_LIMIT_REFILL_PERIOD_SECONDS |
60 | rate.limit.refill.period.seconds |
Refill period for the global bucket |
RATE_LIMIT_REFILL_TOKENS |
100 | rate.limit.refill.tokens |
Tokens to refill |
CACHE_INITIAL_CAPACITY |
100 | cache.initial.capacity |
Cache initial capacity |
CACHE_MAXIMUM_SIZE |
500 | cache.maximum.size |
Maximum size of the cache |
CACHE_EXPIRE_AFTER_WRITE_MINUTES |
1 | cache.expire.after.write.minutes |
Expiration time of the cache |
This table was generated from application.properties
docs
using this script
Integration tests were written using Mockito and Testcontainers and the application was end-to-end tested using Postman.
The application is a working prototype that is standalone and not suitable for real-world use. As of the current version, the following improvements could be made:
- limiting creation of new accounts to a specific group of users
- more detailed tests covering edge cases
- optimization of some operations (mainly inserting and updating data)
- caching improvements
- JWT token refreshing
- and many more
Since anyone can create any type of account, the application is not suitable for real-world use.