2016-08-07 5 views
2

간단한 증명 증명 서번트 API를 설정하는 중에 문제가 있습니다.서번트가있는 데이터베이스 기반 REST API?

data User = User { id :: Int, first_name :: String, last_name :: String } deriving (Eq, Show, Generic) 
instance FromRow User 
instance ToRow User 
$(deriveJSON defaultOptions ''User) 

type API = "users" :> ReqBody '[JSON] User :> Post '[JSON] User 

이에 대한 핸들러 메소드는 다음과 같이 PostgreSQL을 - 간단한 사용 : 그런 된 DB 및 라우팅 방법에 연결하는 등의

create u = liftIO $ head <$> returning connection "insert into users (first_name, last_name) values (?,?) returning id" [(first_name u, last_name u)] 

상용구 코드이 내 사용자의 데이터 형과 내 API 유형입니다 생략했다. 문제는 내가 POST 요청을 할 경우, 내가 새 사용자를 만드는 일을 할 무엇을 원하는, 그래서 나는 JSON 제공 할 것입니다 :

{ "first_name": "jeff", "last_name": "lebowski" } 

하지만 내 프로그램

Error in $: When parsing the record User of type Lib.User the key id was not present. 
와 런타임에 실패합니다

API가 ID 필드가있는 사용자를 지정했기 때문에 이는 의미가 있습니다. 그러나 요청에 bogus id를 넘겨주고 싶지는 않습니다. 왜냐하면 그 요청은 총체적이기 때문에 순차적으로 postgres에 할당되기 때문입니다. Postgres-simple은 다른 엔드 포인트에 GET 요청을 할 때 모델 데이터베이스 불일치로 인해 실패하기 때문에 User 필드에서 id 필드를 이동할 수 없습니다 (이는 id로 얻습니다. 위에 포함되지 않음).). 여기서 무엇을해야합니까? 사용자 정의 FromJson 인스턴스를 작성 하시겠습니까? Data.Aeson.TH 옵션에 omitNothingFields를 True로 설정하고 id 필드를 Maybe Int로 설정했지만 이미 작동하지 않았습니다. 조언을 주시면 감사하겠습니다.

+3

개념이 조금 어둡습니다. 순수 하스켈 만 고려하십시오. 'mkUser :: User -> User'는 의미가 있습니까? 클라이언트가'User'를 만드는 데 필요한 정보만을 가진 메시지를 전달할 수 있도록 허용해야합니다. 예 :'CreateUser = CreateUser {givenName :: Text, surName :: Text}','type API = "users":> ReqBody '[JSON] CreateUser :>'JSON 사용자 '게시. –

+0

필자는이를 고려해 봤지만 모든 엔드 포인트에 대해 추가 데이터 유형을 만들어야하는 것은 비 최적 솔루션이라고 생각합니다.나는 그것을 할 다른 방법이 있기를 바랬다. 이 실제로 구현하고 UserI (UserInput) 같은 패턴을 사용하여 자신을 발견했지만 이것은 하나의 필드 누락을 나타냅니다 및 입력 된 형식과 저장된 형식을 다른 나쁜 생각처럼 보입니다. 리팩토링 사용자가 UserI를 변경하는 것을 잊어 버리면 어떨까요? 하인이 피해야 할 부분이 아닌가? – asg0451

+0

이 POST 요청은 사용자를 서버로 전달한 다음 저장합니다. Semantically 나는 그들을 같은 타입으로 유지하거나 UserI를 User로부터 컴파일 타임에 파생 시키길 원할 것이다. 내 개념이 어색하지 않고, 하스켈에서 경험이 없다. 유형이 의미하는 바를 의미한다는 것을 알았지 만 가장 단순한 API (사용자의 CRUD - 사용자)를 사용하여 개념 증명을 작성하여 서번트 사용을 정당화하려고 시도했습니다. 레일스에서 ​​자동으로 처리됩니다. 그리고 그것이 얼마나 사소한 지에 놀랐습니다. – asg0451

답변

3

먼저이 사용자에 해당하는 테이블의 사용자와 행이 서로 다른 것을 이해해야합니다.

행에 ID가 있으면 사용자가 그렇지 않습니다. 예를 들어, ID를 사용하지 않고 두 명의 사용자를 비교하거나 저장 여부를 비교할 수 있습니다.

일단 확신을 얻은 후에는 이것을 유형 시스템에 설명해야하며, 그렇지 않을 경우 해결할 수없는 필드라고 생각해야합니다.

일부 사람들은 Template Haskell에 대해 이야기했습니다. 나는 그것이 과잉이라고 생각합니다. 먼저 문제를 해결해야합니다.

데이터 형식을 사용하여 데이터베이스에 저장된 행을 나타낼 수 있습니다. 엔티티라고 부르 자.

newtype PrimaryKey = PrimaryKey Int 

data Entity b = Entity PrimaryKey b 

그런 다음 데이터베이스에 사용자 행을 저장하는 함수는 매개 변수로 user을 가지고 (물론, 당신에서 데이터베이스 모나드)를 PrimaryKey를 반환 할 수 있습니다. 데이터베이스에서 읽는 다른 함수는 사용자 유형을 매개 변수로 다시 사용하므로 필드 선언이 중복되지 않습니다. Entity User

필드 선언은 중복되지 않습니다.

따라서 FromRow/ToRow 및 FromJSON/ToJSON을 적절하게 조정해야합니다.

+0

User 객체에 ID 필드가없는 경우 클라이언트가 해당 ID를 필요로하기 때문에 색인이나 show (/ users,/users/: id) 요청에 대한 응답 유형은 Entities도 반환해야합니다. 그래서 그 시점에서 사용자는 무엇에 좋은가요? 내 질문에 POST 요청을 제외하고는 모든 세계와 상호 작용하는 모든 상황에서 해당 ID가 필요하다고 생각됩니다. – asg0451

+0

데이터베이스에있는 사용자가 id를 기본 키로 만 사용하도록 정의했습니다. 이 코드를 코드에 반영하고 싶습니다. 따라서 실제로 ID를 확인하는 사용자 지정 Eq 인스턴스를 추가로 작성해야합니다. 내가 무슨 말하는지 알 겠어? ID는 데이터베이스 시스템의 인공물이 아니라 사용자의 중요한 부분이라고 생각합니다. – asg0451

+0

기본적으로 유형 시스템에서이 id 필드의 구체성을 인코딩하려면 여기를 선택하거나 Maybe를 사용하고 Nothing 값을 처리하십시오. 이것은 동적 언어로 일반적으로 찾는 것이므로 데이터베이스에 저장할 때이 필드에 대한 규칙을 관리해야합니다. 제 경험으로 하스켈에서는 관례에 의한 프로그래밍이 잘 작동하지 않습니다. –