On this post we are focusing on what you need to know about and have experience with in a framework. Specifically, we are assuming that this framework is used as the frontend framework and we have a backend API built with a backend framework like springboot.
#1 Master Language Fundamentals
Data types
Conditional(if) statements
Iteration(loop)
Objects
Arrays
Function
Dates
Network(API) Requests
etc..
#2 Configuration
How to create a project
How to include dependencies like packages your project will need
How to profile your configuration so your project can run in different environments(local, dev, prod) with the right configurations
How to structure your code so that it’s easy to work with
#3 MVC
Routes
Views (HTML & CSS)
Models
Services(API)
#4 Dependency Injection
Dependency Object Container
Dependency Object Lifecycle
How to create a dependency object
How to use a dependency object
#5 Security
Authentication
Authorization
#6 Consume APIs
How to consume APIs
How to mock API calls
#7 Testing
Unit tests with a mock framework
Integration tests
Optionals
You can add the following as you go. Some of these are one time setups and some are devop stuff. Depending on your situation you might have to set these up in case of a small or startup company or they might have been in place already or a devop team is taking care of them.
#8 Cache
A front-end engineer, also known as a front-end web developer, develops, tests, and maintains the user interface of a website. The front-end developer focuses on the look and feel of the site, along with its functionality. He or she works closely with a designer for how the site should look and with a backend engineer for how the site should feel. His main focus is the presentation layer and not necessarily the logic layer. Note that for mobile the title of mobile engineer is often used.
Mach
Spring Data has a starter for Elasticsearch that takes away the boilerplate code of configuring Elasticsearch to work with Spring.
First create a springboot application.
Include this dependency to import Elasticsearch dependencies.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-elasticsearch</artifactId> </dependency>
Configure Elasticsearch.
You can change the host url(localhost:9200) to point to your respective environment like dev, qa, or production when needed.
Make sure to use @EnableElasticsearchRepositories on the configuration class. Also it is important to specify the package where your Elasticsearch repositories are in. Springboot needs to know that so that it cans and configures those repositories and their indexes.
@Configuration @EnableElasticsearchRepositories(basePackages = "com.lovemesomecoding.es.repository") public class ElasticsearchConfig { @Bean public RestHighLevelClient client() { ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200").build(); return RestClients.create(clientConfiguration).rest(); } @Bean public ElasticsearchOperations elasticsearchTemplate() { return new ElasticsearchRestTemplate(client()); } }
Create your index repository using ElasticsearchRepository.
package com.lovemesomecoding.es.repository; import java.util.List; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; import com.lovemesomecoding.es.user.User; public interface UserRepository extends ElasticsearchRepository<User, Long> { List<User> findByLastName(String lastName); }
Create your index
I prefer using Spring Data annotations to create and manage my index. This allows Spring Data to manage index mapping, field type, etc which is time consuming I were to do it myself. When your application boots up it will automatically create or update your index and its mapping based on your Index class. Without this you would have to manually create/update index when there are new fields to add.
@Data @AllArgsConstructor @NoArgsConstructor @JsonInclude(value = Include.NON_NULL) @JsonAutoDetect(fieldVisibility = Visibility.ANY) @Document(indexName = "sb_with_es_user") public class User implements Serializable { private static final long serialVersionUID = 1L; @Id private String id; @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)}) private String firstName; @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)}) private String lastName; @Field(type = FieldType.Keyword) private String middleName; @MultiField(mainField = @Field(type = FieldType.Keyword), otherFields = {@InnerField(suffix = "token", type = FieldType.Text)}) private String email; }
Create your DAO(Data Access Object).
It is best practice to have a DAO per index so that you can isolate changes and your code is readable. Having a DAO allows you to wire in your repository(UserRepository) and RestHighLevelClient. With your repository you can take advantage of what Spring Data has developed for you to use. You also has RestHighLevelClient that you can use as a pure java client to query Elasticsearch.
@Repository @Slf4j public class UserDAOImp implements UserDAO { @Autowired private UserRepository userRepository; @Autowired private RestHighLevelClient restHighLevelClient; @Override public List<User> getAllUsers() { return null; } @Override public List<User> getUsersByFirstName(String firstName) { int pageNumber = 0; int pageSize = 10; SearchRequest searchRequest = new SearchRequest("sb_with_es_user"); searchRequest.allowPartialSearchResults(true); searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen()); SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); searchSourceBuilder.from(pageNumber * pageSize); searchSourceBuilder.size(pageSize); searchSourceBuilder.timeout(new org.elasticsearch.core.TimeValue(60, TimeUnit.SECONDS)); /** * fetch only a few fields */ // searchSourceBuilder.fetchSource(new String[]{ "id", "firstName", "lastName", "cards" }, new String[]{""}); /** * Query */ /** * Filter<br> * term query looks for exact match. Use keyword */ searchSourceBuilder.query(QueryBuilders.termQuery("firstName", firstName)); searchRequest.source(searchSourceBuilder); searchRequest.preference("firstName"); if (searchSourceBuilder.sorts() != null && searchSourceBuilder.sorts().size() > 0) { log.info("\n{\n\"query\":{}, \"sort\":{}\n}", searchSourceBuilder.query().toString(), searchSourceBuilder.sorts().toString()); } else { log.info("\n{\n\"query\":{}\n}", searchSourceBuilder.query().toString()); } List<User> users = null; try { SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); log.info("isTimedOut={}, totalShards={}, totalHits={}", searchResponse.isTimedOut(), searchResponse.getTotalShards(), searchResponse.getHits().getTotalHits().value); users = getResponseResult(searchResponse.getHits()); log.info(ObjectUtils.toJson(users)); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return users; } private List<User> getResponseResult(SearchHits searchHits) { Iterator<SearchHit> it = searchHits.iterator(); List<User> searchResults = new ArrayList<>(); while (it.hasNext()) { SearchHit searchHit = it.next(); // log.info("sourceAsString={}", searchHit.getSourceAsString()); try { User obj = ObjectUtils.getObjectMapper().readValue(searchHit.getSourceAsString(), new TypeReference<User>() {}); // log.info("obj={}", ObjectUtils.toJson(obj)); searchResults.add(obj); } catch (IOException e) { log.warn("IOException, msg={}", e.getLocalizedMessage()); } } return searchResults; } @Override public List<User> getUsersByLastName(String lastName) { return userRepository.findByLastName(lastName); } }