I am (desperately) trying to build a simple database example with a one-to-many relationship. But at the end, when I try to create a children entity I receive the following error, which I am unable to understand.
[ WARNING ] Value was not of type 'User' at path 'user'. Could not decode property. Underlying error: valueNotFound(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [SomeCodingKey(stringValue: "user", intValue: nil)], debugDescription: "Cannot get keyed decoding container -- found null value instead", underlyingError: nil)). [method: POST, request-id: A8F743CD-84B5-4540-99A2-5CB8EA25707C, url: /users/6FB9253B-56BE-4F7C-ABC6-2CB72C22B476/todos, userAgent: [curl/8.7.1]]
Creating a user with:
curl -X POST http://127.0.0.1:8080/users -H "Content-Type: application/json" -d '{"name":"Bob","email":"bob@example.com"}'
returns as expected :
{"email":"bob@example.com","id":"6FB9253B-56BE-4F7C-ABC6-2CB72C22B476","name":"Bob"}**%**
but, trying to add a task with:
curl -X POST http://127.0.0.1:8080/users/6FB9253B-56BE-4F7C-ABC6-2CB72C22B476/todos -H "Content-Type: application/json" -d '{"title":"Write Code","user_id":"6FB9253B-56BE-4F7C-ABC6-2CB72C22B476"}'
Produces the above mentionned message. It must be a simple mistake somewhere but I am unable to find out where. Would appreciate some help.
Here is my User model :
final class User: Model, @unchecked Sendable, Content {
static let schema = "users"
@ID(key: .id)
var id: UUID?
@Field(key:"name")
var name: String
@Field(key:"email")
var email: String
@Children(for: \.$user)
var todos: [Todo]
init(){}
init(id: UUID? = nil, name: String, email: String) {
self.id = id
self.name = name
self.email = email
}
}
My todo Model:
final class Todo: Model, @unchecked Sendable, Content {
static let schema = "todos"
@ID(key: .id)
var id: UUID?
@Field(key: "title")
var title: String
@Parent(key: "user_id")
var user: User
init() { }
init(id: UUID? = nil, title: String, userID: User.IDValue) {
self.id = id
self.title = title
self.$user.id = userID
}
}
The migrations
struct CreateUser: AsyncMigration {
func prepare(on database: any Database) async throws {
try await database.schema("users")
.id()
.field("name", .string)
.field("email", .string)
.create()
}
func revert(on database: any Database) async throws {
try await database.schema("users").delete()
}
}
struct CreateTodo: AsyncMigration {
func prepare(on database: any Database) async throws {
try await database.schema("todos")
.id()
.field("title", .string)
.field("user_id", .uuid, .references("users", "id"))
.create()
}
func revert(on database: any Database) async throws {
try await database.schema("todos").delete()
}
}
and the user controller
struct UserController: RouteCollection {
func boot(routes: any RoutesBuilder) throws {
let users = routes.grouped("users")
users.post(use: self.create)
users.post(":userID","todos", use: self.createTodo)
users.get(":userID","todos", use: self.getTodos)
}
@Sendable
func create(req: Request) async throws -> User {
let user = try req.content.decode(User.self)
try await user.save(on: req.db)
return user
}
@Sendable
func createTodo(req: Request) async throws -> Todo {
let todo = try req.content.decode(Todo.self)
try await todo.save(on :req.db)
return todo
}
@Sendable
func getTodos(req: Request) async throws -> [Todo] {
let userID = try req.parameters.require("userID", as: UUID.self)
guard let user = try await User.find(userID, on: req.db) else {
throw Abort(.notFound, reason: "User not found")
}
return try await user.$todos.query(on: req.db).all()
}
}