Survey management service with possibility to create questionnaires, submit answers and gather data that becomes open and reusable for everyone.
Go 1.26.1, Postgres, and a Redis server are required. Tests run against a Postgres test database. JWT authentication is for protected submission and cart endpoints. Cart items are stored as JSON in Redis under keys prefixed with cart:<user_id>
- Clone the repository.
- Build or run with go tools:
go build -o survey-service .
./survey-service
# or
go run main.go- The server starts on
:8080by default.
Use Docker Compose to start the app, Postgres, and Redis together:
docker compose up --buildDATABASE_URL, JWT_SECRET, JWT_ISSUER, and JWT_AUDIENCE are expected in the environment. See .env.example.
Required for DB and JWT configuration:
-
DATABASE_URL— Postgres connection string -
DATABASE_URL_TEST— Postgres test database connection string -
JWT_SECRET— HMAC secret used to validate tokens -
JWT_ISSUER— expected token issuer -
JWT_AUDIENCE— expected token audience -
ALLOWED_EMAIL_DOMAINS- comma-separated list of allowed registration domains
Optional for Redis:
-
REDIS_ADDRESS— Redis server address (default:localhost:6379) -
POST /register -
Body:
{
"email": "user@example.com",
"password": "strongPassword123"
}- Validation rules:
- Email must be valid and its domain must be listed in
ALLOWED_EMAIL_DOMAINS - Password must be 8 to 72 characters long
- Password must contain ASCII characters only
- Email must be valid and its domain must be listed in
Successful registration returns HTTP 200 with an empty body.
POST /login- Body:
{
"email": "user@example.com",
"password": "strongPassword123"
}- Response example:
{
"message": "logged in",
"token": "<jwt>",
"email": "user@example.com",
"user_id": "<uuid>",
"role": "user",
"expires": "2026-05-13T10:00:00Z"
}The token is valid for 12 hours and is signed with JWT_SECRET.
Send the JWT in the Authorization header when calling protected routes introduced in future revisions:
Authorization: Bearer <token>main.go— application entrypoint and HTTP route definitionsinternal/handlers— HTTP handlers (create survey, list surveys, submissions)internal/repository— database access and schemainternal/models— domain models (Survey, Question, Submission)internal/dto— request/response DTOs and conversionsinternal/auth— JWT token validation and claimsinternal/validations— request decoding and validation helpersinternal/testutil— Utilities for test suites (setting up test db, Redis, etc)
GET /— health / placeholderGET /surveys— list surveys- Response: array of surveys
{ id, name, description, created_at }
- Response: array of surveys
POST /survey— create a survey- Body:
RequestCreateSurvey(seeinternal/dto) - Response: created survey object and message (201)
- Body:
GET /survey/{surveyId}— retrieve a single survey by idDELETE /survey/{surveyId}— delete a survey by idGET /catalog/surveys/{surveyId}/submissions— list public submissions for a survey (anonymous)
Survey Submissions
POST /survey/{surveyId}/submissions— submit answers for a survey- Body:
RequestCreateSubmission(answers array withquestion_id, optionalchoice_id, optionaltext_response) - Requires a valid JWT; token's
user_idis used as the submitter - Submissions are public by default for the catalog
- Response: created submission (201)
- Body:
GET /survey/{surveyId}/submissions— list submissions for a survey- Admin users (token claim
role==admin) can retrieve all submissions; otherwise only submissions for the token user are returned
- Admin users (token claim
GET /users/{userId}/submissions— list submissions for a user- Admins or the user themself may access this endpoint
Shopping Cart (stored in Redis)
POST /cart/items— add an item to user's cart- Body:
{ "item": { "survey_id", "question_id", "submission_id" (optional), "answer_id" (optional), "note" (optional) } } - Response: 201 Created
- Body:
GET /cart— retrieve user's cart items- Query parameters:
limit(default 50),offset(default 0) for pagination - Response: array of cart items
- Query parameters:
DELETE /cart/items/{index}— remove item at specific index from cart- Response: 200 OK
DELETE /cart— clear entire user's cart- Response: 200 OK
GET /survey/{surveyId}/submissions— list submissions for a survey- Admin users (token claim
role==admin) can retrieve all submissions; otherwise only submissions for the token user are returned
- Admin users (token claim
GET /users/{userId}/submissions— list submissions for a user- Admins or the user themself may access this endpoint
Protect endpoints by including a JWT in the Authorization header:
Authorization: Bearer <token>
The token should contain claims compatible with internal/auth.AccessClaims (email, user_id, role).
Create a survey (example):
curl -X POST http://localhost:8080/survey \
-H "Content-Type: application/json" \
-d '{
"name": "Example",
"description": "Example survey",
"questions_list": [
{"description":"How are you?","type":1,"is_mandatory":false}
]
}'Submit answers (example, protected):
curl -X POST http://localhost:8080/survey/<survey-id>/submissions \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"answers":[{"question_id":"<q-id>","text_response":"Good"}]}'Public catalog (examples):
# List public submissions for a survey (first 50)
curl http://localhost:8080/catalog/surveys/<survey-id>/submissions
# List public submissions with pagination
curl http://localhost:8080/catalog/surveys/<survey-id>/submissions?limit=100&offset=0
# List public answers for a question
curl http://localhost:8080/catalog/questions/<question-id>/answers?limit=100&offset=0