This is a very simple web API for a school assignment, as a part of a bigger system. The web API returns a list of courses or a single course as json. I doubt that I will get any feedback on the code since Java is taught in the course so I am posting it here.
I am a beginner in Racket but have experience with C-style languages (C, C#, Python, etc).
- I want to know how I can improve my solution and to be more idiomatic?
- My function for converting the row-result from the database query to json (
rows-result->json) is very messy, how can improve this? I have added example results (mock-row-result) from the database query that can be used as an argument.
The web api has two end points:
- GET /course/{courseid}
- GET /course with optional parameters: CourseCode, StudyPeriod
The database is a single table in an sqlite-database with the rows:
- id - int
- AppCode - text
- CourseCode - text
- StudyPeriod - text
- Name - text
Code:
#lang racket
(require web-server/servlet
web-server/servlet-env
web-server/dispatch
db
json
net/url-structs)
(define PORT 8081)
(define DATABASE "paraplyet.db")
; Database connection
(define paraplyet-db
(sqlite3-connect #:database DATABASE))
; Convert results from database to json, messy
(define (rows-result->json result)
(define column-names
(map (lambda (r) (string->symbol (cdr (first r)))) (rows-result-headers result)))
(define rows-lists (map vector->list (rows-result-rows result)))
; Create a list of hashmaps to be able to use jsexpr->string
(define result-hashmaps
(map (lambda (row) (make-hash (map cons column-names row))) rows-lists))
(jsexpr->string result-hashmaps))
; Mock results from database query, can be used for the function rows-result->json
(define mock-row-result (rows-result
'(((name . "AppCode") (decltype . "TEXT"))
((name . "CourseCode") (decltype . "TEXT"))
((name . "StudyPeriod") (decltype . "TEXT"))
((name . "Name") (decltype . "TEXT")))
'(#("LTU-37067" "D0004N" "HT2018" "Databaser I")
#("LTU-17045" "D0020N" "HT2018" "Utveckling av informationssystem"))))
; Return list of courses with optional filtering the the parameters CourseCode and StudyPeriod
(define (get-courses req)
(define params (make-hash (url-query (request-uri req))))
(define courses (query paraplyet-db "SELECT AppCode, CourseCode, StudyPeriod, Name
FROM Course
WHERE CourseCode = UPPER(COALESCE($1, CourseCode)) AND
StudyPeriod = UPPER(COALESCE($2, StudyPeriod))"
(hash-ref params 'CourseCode sql-null)
(hash-ref params 'StudyPeriod sql-null)))
; Respond with json
(response/xexpr (rows-result->json courses)
#:mime-type (string->bytes/utf-8 "application/json")))
; Return a specific course
(define (get-course req app-code)
(define courses (query paraplyet-db "SELECT AppCode, CourseCode, StudyPeriod, Name
FROM Course
WHERE AppCode = $1" app-code))
; Return 404 if course is missing, otherwise return JSON
(if (empty? (rows-result-rows courses))
(response/xexpr "404: Resource not found" #:code 404)
(response/xexpr (rows-result->json courses)
#:mime-type (string->bytes/utf-8 "application/json"))))
; Function to respond with 404
(define (not-found req)
(response/xexpr "404: Not Found"
#:code 404))
; Definition of routing
; /course Get many courses (filter with parameters)
; /course/{AppCode} Get course with specific AppCode
(define-values (go _)
(dispatch-rules
[("course" (string-arg)) #:method "get" get-course]
[("course") #:method "get" get-courses]
[else not-found]))
(define (start-server)
(serve/servlet go
#:port PORT
#:servlet-regexp #rx""
#:launch-browser? #f
))
(start-server)