A production-quality starter app using Actix 1.x. Includes tests and coverage. Diesel was intentionally omitted to add in flexibility for usage. A full framework that includes Diesel is being developed at https://github.com/ddimaria/rust-actix-framework.
I needed a quick way to bootstrap an Actix repo for various projects that had all of the baseline, production-ready features baked in.
- Actix 1.x HTTP Server
- Filesystem organized for scale
- .env for local development
- Lazy Static Config struct
- Built-in Healthcheck (includes cargo version info)
- Listeners configured for TDD
- Custom Errors and HTTP Payload/Json Validation
- Unit and Integration Tests
- Test Coverage Reports
- Dockerfile for Running the Server in a Container
- TravisCI Integration
actix-web
: Actix Web Serverderive_more
: Error Formattingdotenv
: Configuration Loader (.env)envy
: Deserializes Environment Variables into a Config Structlistenfd
: Listens for Filesystem Changesvalidator
: Validates incoming Jsonkcov
: Coverage Analysis
Clone the repo and cd into the repo:
git clone https://github.com/ddimaria/rust-actix-starter.git
cd rust-actix-starter
Copy over the example .env file:
cp .env.example .env
To startup the server:
cargo run
To startup the server and autoreload on code changes:
systemfd --no-pid -s http::3000 -- cargo watch -x run
Integration tests are in the /src/tests
folder. There are helper functions
to make testing the API straightforward. For example, if we want to test the
GET /api/v1/user
route:
use crate::tests::helpers::tests::assert_get;
#[test]
fn test_get_users() {
assert_get("/api/v1/user");
}
Using the Actix test server, the request is sent and the response is asserted for a successful response:
assert!(response.status().is_success());
Similarly, to test a POST route:
use crate::handlers::user::CreateUserRequest;
use crate::tests::helpers::tests::assert_post;
#[test]
fn test_create_user() {
let params = CreateUserRequest {
first_name: "Satoshi".into(),
last_name: "Nakamoto".into(),
email: "satoshi@nakamotoinstitute.org".into(),
};
assert_post("/api/v1/user", params);
}
To run all of the tests:
cargo test
I created a repo on DockerHub that I'll update with each Rust version (starting at 1.37), whose tags will match the Rust version.
In the root of the project:
docker run -it --rm --security-opt seccomp=unconfined --volume "${PWD}":/volume --workdir /volume ddimaria/rust-kcov:1.37 --exclude-pattern=/.cargo,/usr/lib,/src/main.rs,src/server.rs
note: converage takes a long time to run (up to 30 mins).
You can view the HTML output of the report at target/cov/index.html
To build a Docker image of the application:
docker build -t rust_actix_starter .
Once the image is built, you can run the container in port 3000:
docker run -it --rm --env-file=.env.docker -p 3000:3000 --name rust_actix_starter rust_actix_starter
Determine if the system is healthy.
GET /health
{
"status": "ok",
"version": "0.1.0"
}
Example:
curl -X GET http://127.0.0.1:3000/health
GET /api/v1/user
[
{
"id": "a421a56e-8652-4da6-90ee-59dfebb9d1b4",
"first_name": "Satoshi",
"last_name": "Nakamoto",
"email": "satoshi@nakamotoinstitute.org"
},
{
"id": "c63d285b-7794-4419-bfb7-86d7bb3ff17d",
"first_name": "Barbara",
"last_name": "Liskov",
"email": "bliskov@substitution.org"
}
]
Example:
curl -X GET http://127.0.0.1:3000/api/v1/user
GET /api/v1/user/{id}
Param | Type | Description |
---|---|---|
id | Uuid | The user's id |
{
"id": "a421a56e-8652-4da6-90ee-59dfebb9d1b4",
"first_name": "Satoshi",
"last_name": "Nakamoto",
"email": "satoshi@nakamotoinstitute.org"
}
Example:
curl -X GET http://127.0.0.1:3000/api/v1/user/a421a56e-8652-4da6-90ee-59dfebb9d1b4
404 Not Found
{
"errors": ["User c63d285b-7794-4419-bfb7-86d7bb3ff17a not found"]
}
POST /api/v1/user
Param | Type | Description | Required | Validations |
---|---|---|---|---|
first_name | String | The user's first name | yes | at least 3 characters |
last_name | String | The user's last name | yes | at least 3 characters |
String | The user's email address | yes | valid email address |
{
"first_name": "Linus",
"last_name": "Torvalds",
"email": "torvalds@transmeta.com"
}
{
"id": "0c419802-d1ef-47d6-b8fa-c886a23d61a7",
"first_name": "Linus",
"last_name": "Torvalds",
"email": "torvalds@transmeta.com"
}
Example:
curl -X POST \
http://127.0.0.1:3000/api/v1/user \
-H 'Content-Type: application/json' \
-d '{
"first_name": "Linus",
"last_name": "Torvalds",
"email": "torvalds@transmeta.com"
}'
422 Unprocessable Entity
{
"errors": [
"first_name is required and must be at least 3 characters",
"last_name is required and must be at least 3 characters",
"email must be a valid email"
]
}
This project is licensed under:
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)